/*
 * Created on Jan 2, 2005
 *
 * Copyright (c) Patrick Armstrong 2002-2007. All Rights Reserved
 */
package org.dime.mine.ui;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.concurrent.Semaphore;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;

import org.dime.mine.bot.MineBot;
import org.dime.mine.game.Board;
import org.dime.mine.game.BoardEvent;
import org.dime.mine.game.BoardListener;
import org.dime.mine.game.GameOutcomeListener;

/**
 * Main // TODO Add type summary
 * 
 * @author Owner
 */
public class Main
{
    // TODO implement logging
    // TODO implement colored solution diagrams
    private GraphicalBoard board;
    private MineBotThread mbt;

    private JFrame mainFrame = new JFrame();
    private final JLabel statusBar = new JLabel();

    public Main()
    {
        createUI();
        startNewGame(16, 30, 99);

        this.mainFrame.setVisible(true);
    }

    private void createUI()
    {
        this.mainFrame = new JFrame("Mine");

        JMenuBar menuBar = new JMenuBar();
        JMenu gameMenu = new JMenu("Game");
        JMenuItem newGameItem = new JMenuItem("New   n");
        newGameItem.addActionListener(new NewGameItemHandler());
        gameMenu.add(newGameItem);
        menuBar.add(gameMenu);

        JMenu botMenu = new JMenu("Bot");
        JMenuItem assistItem = new JMenuItem("Assist   a");
        assistItem.addActionListener(new AssistItemHandler());
        botMenu.add(assistItem);
        menuBar.add(botMenu);

        this.mainFrame.setJMenuBar(menuBar);
        this.mainFrame.add(BorderLayout.SOUTH, statusBar);
        this.mainFrame.addKeyListener(new NewGameItemHandler());
        this.mainFrame.addKeyListener(new AssistItemHandler());
        this.mainFrame.setResizable(false);
        this.mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        this.mainFrame.pack();
    }

    private void startNewGame(int rows, int columns, int mines)
    {
        GraphicalBoard gb = makeNewGraphicalBoard(rows, columns, mines);
        resetMineBot(gb);
        resetUI(gb);
        this.board = gb;
    }

    private GraphicalBoard makeNewGraphicalBoard(int rows, int columns,
            int mines)
    {
        GraphicalBoard gb = new GraphicalBoard(rows, columns, mines);
        // GraphicalBoard gb = new GraphicalBoard(new int[][] {
        // { 0, 0, 9, 0, 0},
        // { 0, 0, 0, 0, 0},
        // { 0, 0, 0, 0, 0},
        // { 9, 0, 0, 0, 9},
        // { 0, 0, 0, 0, 0},
        // { 0, 0, 0, 0, 0},
        // { 0, 0, 9, 0, 0} });
        gb.addBoardListener(new BoardUpdateHandler());
        gb.addGameListener(new GameOverHandler());
        // ToolTipHandler tth = new ToolTipHandler(gb);
        // gb.addMouseListener(tth);
        // gb.addBoardListener(tth);
        return gb;
    }

    private void resetUI(GraphicalBoard board)
    {
        if (this.board != null)
            this.mainFrame.remove(this.board);

        this.mainFrame.add(board, BorderLayout.CENTER);
        setStatusBarText(board.getMines());
        this.mainFrame.pack();
        this.mainFrame.repaint();
    }

    private void resetMineBot(GraphicalBoard board)
    {
        if (mbt == null)
            mbt = new MineBotThread(board);
        else
            mbt.setBoard(board);

        if (mbt.getState() == Thread.State.NEW)
            mbt.start();
    }

    private void setStatusBarText(int text)
    {
        statusBar.setText("" + text);
    }

    private class MineBotThread extends Thread
    {
        // TODO check synchronization between player and MineBot

        private final Semaphore s;
        private final MineBot mb;
        private boolean released = false;

        public MineBotThread(Board b)
        {
            super("MineBotThread");
            this.mb = new MineBot(b);
            this.mb.setStrategyEnabled(MineBot.GUESS, false);
            this.s = new Semaphore(0, true);
        }

        @Override
        public void run()
        {
            while (!isInterrupted())
            {
                try
                {
                    s.acquire();
                    synchronized (this)
                    {
                        released = false;
                        if (!board.isGameOver())
                            while (mb.next() && !board.isGameOver())
                                ;
                    }
                }
                catch (InterruptedException ie)
                {
                    this.interrupt();
                }
            }
        }

        public void next()
        {
            synchronized (this)
            {
                if (released)
                    return;
                else
                    released = true;
            }
            s.release();
        }

        public synchronized void setBoard(Board b)
        {
            try
            {
                if (released)
                    s.acquire();
                mb.setBoard(b);
            }
            catch (InterruptedException ie)
            {}
        }
    }

    private class NewGameItemHandler implements ActionListener, KeyListener
    {
        public void actionPerformed(ActionEvent e)
        {
            startNewGame(16, 30, 99);
        }

        public void keyPressed(KeyEvent e)
        {}

        public void keyReleased(KeyEvent e)
        {}

        public void keyTyped(KeyEvent e)
        {
            if (e.getKeyChar() == 'n')
                startNewGame(16, 30, 99);
        }
    }

    private class AssistItemHandler implements ActionListener, KeyListener
    {
        public void actionPerformed(ActionEvent e)
        {
            mbt.next();
        }

        public void keyPressed(KeyEvent e)
        {}

        public void keyReleased(KeyEvent e)
        {}

        public void keyTyped(KeyEvent e)
        {
            if (e.getKeyChar() == 'a')
                mbt.next();
        }
    }

    private class BoardUpdateHandler implements BoardListener
    {
        public void locationMarked(BoardEvent be)
        {
            setStatusBarText(board.getMines() - board.getMarks());
        }

        public void locationOpened(BoardEvent be)
        {}

        public void locationUnmarked(BoardEvent be)
        {
            setStatusBarText(board.getMines() - board.getMarks());
        }
    }

    private class GameOverHandler implements GameOutcomeListener
    {
        public void gameOver()
        {}

        public void gameLost()
        {
            statusBar.setText("Lost");
        }

        public void gameWon()
        {
            statusBar.setText("Won");
        }

        public void gameDrawn()
        {}
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                JFrame.setDefaultLookAndFeelDecorated(true);
                new Main();
            }
        });
    }
}